﻿#include <QTimer>
#include <QJsonDocument>
#include <QJsonObject>
#include "JZNodeEvent.h"
#include "JZNodeCompiler.h"
#include "JZNodeFunctionManager.h"
#include "JZNodeBind.h"
#include "JZNodeUtils.h"

//JZNodeSignalConnect
JZNodeSignalConnect::JZNodeSignalConnect()
{
    m_type = Node_signalConnect;
    m_name = "connect";

    addFlowIn();
    addFlowOut();

    int in1 = addParamIn("sender");
    int in2 = addParamIn("signal", Pin_constValue);
    int in3 = addParamIn("receiver");
    int in4 = addParamIn("slot", Pin_constValue);

    setPinType(in1, { JZNodeType::typeName(Type_object) });
    setPinType(in2, { JZNodeType::typeName(Type_function) });
    setPinType(in3, { JZNodeType::typeName(Type_object) });
    setPinType(in4, { JZNodeType::typeName(Type_function) });
}

JZNodeSignalConnect::~JZNodeSignalConnect()
{

}

bool JZNodeSignalConnect::compiler(JZNodeCompiler* c, QString& error)
{
    if (!c->addFlowInput(m_id, error))
        return false;

    QString sig = c->pinLiteral(m_id, paramIn(1));
    QString slot = c->pinLiteral(m_id, paramIn(3));

    auto env = environment();
    auto sig_func = env->objectManager()->signal(sig);
    auto slot_func = env->functionManager()->function(slot);
    if (!sig_func)
    {
        error = "no signal " + sig;
        return false;
    }
    if (!slot_func)
    {
        error = "no slot " + slot;
        return false;
    }
    if (!JZNodeType::sigSlotTypeMatch(sig_func, slot_func))
    {
        error = "signal slot not match " + sig_func->fullName() + "," + slot_func->delcare();
        return false;
    }

    JZFunctionPointer sig_ptr(sig);
    JZFunctionPointer slot_ptr(slot);

    int send_id = c->paramId(m_id, paramIn(0));
    int recv_id = c->paramId(m_id, paramIn(2));
    QList<JZNodeIRParam> in, out;
    in << irId(send_id) << irLiteral(QVariant::fromValue(sig_ptr)) << irId(recv_id) << irLiteral(QVariant::fromValue(slot_ptr));
    c->addCall("connect", in, out);

    return true;
}

//JZNodeSignalDisconnect
JZNodeSignalDisconnect::JZNodeSignalDisconnect()
{
}

JZNodeSignalDisconnect::~JZNodeSignalDisconnect()
{
}

bool JZNodeSignalDisconnect::compiler(JZNodeCompiler* c, QString& error)
{
    return false;
}

// JZNodeEvent
JZNodeEvent::JZNodeEvent()
{
    m_type = Node_none;

    addFlowOut();
}

JZNodeEvent::~JZNodeEvent()
{

}

const JZNodeObjectDefine* JZNodeEvent::classMeta()
{
    auto class_item = m_file->getClassItem();
    if (!class_item)
        return nullptr;

    return environment()->meta(class_item->className());
}

//JZNodeFunctionStart
JZNodeFunctionStart::JZNodeFunctionStart()
{
    m_name = "Start";
    m_type = Node_functionStart;
    setFlag(NodeProp_noRemove);
}

JZNodeFunctionStart::~JZNodeFunctionStart()
{

}

JZFunctionDefine JZNodeFunctionStart::function()
{
    if (!m_file)
        return JZFunctionDefine();

    return m_file->function();
}

bool JZNodeFunctionStart::compiler(JZNodeCompiler *c, QString &error)
{    
    c->addFunctionAlloc(m_file->function());
    c->addNodeEnter(m_id);
    c->addFlowOutput(m_id);
    return true;
}

//JZNodeShowEvent
JZNodeShowEvent::JZNodeShowEvent()
{
    m_name = "ShowEvent";
    m_type = Node_showEvent;
}

JZFunctionDefine JZNodeShowEvent::function()
{
    return JZFunctionDefine();
}
bool JZNodeShowEvent::compiler(JZNodeCompiler* compiler, QString& error)
{
    return false;
}

//JZNodeCloseEvent
JZNodeCloseEvent::JZNodeCloseEvent()
{
    m_name = "CloseEvent";
    m_type = Node_closeEvent;
}

JZFunctionDefine JZNodeCloseEvent::function()
{
    return JZFunctionDefine();
}

bool JZNodeCloseEvent::compiler(JZNodeCompiler* compiler, QString& error)
{
    return false;
}

//JZNodeResizeEvent
JZNodeResizeEvent::JZNodeResizeEvent()
{
    m_name = "ResizeEvent";
    m_type = Node_resizeEvent;
}

JZFunctionDefine JZNodeResizeEvent::function()
{
    return JZFunctionDefine();
}

bool JZNodeResizeEvent::compiler(JZNodeCompiler* compiler, QString& error)
{
    return false;
}

//JZNodePaintEvent
JZNodePaintEvent::JZNodePaintEvent()
{
    m_name = "PaintEvent";
    m_type = Node_paintEvent;
}

JZFunctionDefine JZNodePaintEvent::function()
{
    return JZFunctionDefine();
}
bool JZNodePaintEvent::compiler(JZNodeCompiler* compiler, QString& error)
{
    return false;
}

//JZNodeMousePressEvent
JZNodeMousePressEvent::JZNodeMousePressEvent()
{
    m_name = "MousePressEvent";
    m_type = Node_mousePressEvent;
}

JZFunctionDefine JZNodeMousePressEvent::function()
{
    return JZFunctionDefine();
}
bool JZNodeMousePressEvent::compiler(JZNodeCompiler* compiler, QString& error)
{
    return false;
}

//JZNodeMouseReleaseEvent
JZNodeMouseReleaseEvent::JZNodeMouseReleaseEvent()
{
    m_name = "MouseReleaseEvent";
    m_type = Node_mouseReleaseEvent;
}

JZFunctionDefine JZNodeMouseReleaseEvent::function()
{
    return JZFunctionDefine();
}
bool JZNodeMouseReleaseEvent::compiler(JZNodeCompiler* compiler, QString& error)
{
    return false;
}

//JZNodeMouseMoveEvent
JZNodeMouseMoveEvent::JZNodeMouseMoveEvent()
{
    m_name = "MouseMoveEvent";
    m_type = Node_mouseMoveEvent;
}

JZFunctionDefine JZNodeMouseMoveEvent::function()
{
    return JZFunctionDefine();
}

bool JZNodeMouseMoveEvent::compiler(JZNodeCompiler* compiler, QString& error)
{
    return false;
}

//JZNodeKeyPressEvent
JZNodeKeyPressEvent::JZNodeKeyPressEvent()
{
    m_name = "KeyPressEvent";
    m_type = Node_keyPressEvent;
}

JZFunctionDefine JZNodeKeyPressEvent::function()
{
    return JZFunctionDefine();
}

bool JZNodeKeyPressEvent::compiler(JZNodeCompiler* compiler, QString& error)
{
    return false;
}

//JZNodeKeyReleaseEvent
JZNodeKeyReleaseEvent::JZNodeKeyReleaseEvent()
{
    m_name = "KeyReleaseEvent";
    m_type = Node_keyReleaseEvent;
}

JZFunctionDefine JZNodeKeyReleaseEvent::function()
{
    return JZFunctionDefine();
}

bool JZNodeKeyReleaseEvent::compiler(JZNodeCompiler* compiler, QString& error)
{
    return false;
}

//JZNodeSignalEvent
JZFunctionDefine JZNodeSignalEvent::function()
{
    auto meta = classMeta();
    if (!meta)
        return JZFunctionDefine();

    QString file_name = m_file->name();
    JZFunctionDefine define = meta->initMemberFunction(m_event + "_" + QString::number(m_id) + "_" + file_name);
    auto list = paramOutList();
    for (int i = 0; i < list.size(); i++)
    {
        auto pin = this->pin(list[i]);
        Q_ASSERT(pin->dataType().size() == 1);

        define.paramIn << JZParamDefine(pin->name(), pin->dataType()[0]);
    }    
    return define;    
}

bool JZNodeSignalEvent::compilerSignal(JZNodeCompiler* c, QString& error)
{
    auto meta = classMeta();
    if (!meta || !meta->isInherits(Type_object))
    {
        error = "only support define in object";
        return false;
    }

    auto func_def = function();
    c->addFunctionAlloc(func_def);
    c->addNodeEnter(m_id);
    c->addClassInitFunction(m_connectInfo);    

    c->setRegCallFunction(&func_def);
    auto out_list = paramOutList();
    for (int i = 0; i < out_list.size(); i++)
    {
        int out_id = c->paramId(m_id, out_list[i]);
        c->addSetVariable(irId(out_id), irRef(func_def.paramIn[i+1].name)); //跳过this
    }
    c->setRegCallFunction(nullptr);

    c->addFlowOutput(m_id);
    return true;
}

//JZNodeButtonClickedEvent
JZNodeButtonClickedEvent::JZNodeButtonClickedEvent()
{
    m_type = Node_buttonClickedEvent;
    m_name = "timerEvent";

    m_connectInfo.function = "JZButtonClickedEventConnect";
    m_connectInfo.irList << irThis() << irThis() << irLiteral(0);

    int id = addParamIn("name",Pin_noCompiler);
    setPinTypeString(id);
}

JZNodeButtonClickedEvent::~JZNodeButtonClickedEvent()
{
}

void JZNodeButtonClickedEvent::setObject(QString name)
{
    setParamInValue(0, name);
}

QString JZNodeButtonClickedEvent::object()
{
    return paramInValue(0);
}

bool JZNodeButtonClickedEvent::compiler(JZNodeCompiler* c, QString& error)
{
    QString name = object();
    if (!c->checkVariableType(name, "QPushButton", error))
        return false;

    m_connectInfo.irList[2] = irLiteral(QVariant::fromValue(JZFunctionPointer(function().fullName())));
    return compilerSignal(c, error);
}

void JZButtonClickedEventConnect(QObject* object, JZFunctionPointer slot_function, const QByteArray& buffer)
{
    QJsonObject obj = JZNodeUtils::jsonDecode(buffer);
    int ms = obj["timeout"].toInt();

    QTimer* timer = new QTimer(object);
    timer->connect(timer, &QTimer::timeout, object, [object, slot_function]
        {
            JZNodeObject* jzobj = qobjectToJZObject(object);
            JZNodeObjectPointer self(jzobj, false);
            QVariantList in, out;
            in << QVariant::fromValue(self);
            JZScriptInvoke(slot_function.function, in, out);
        });
    timer->start(ms);
}

//JZNodeTimerEvent
JZNodeTimerEvent::JZNodeTimerEvent()
{
    m_timeout = 1000;
    m_type = Node_timerEvent;
    m_name = "timerEvent";

    m_connectInfo.function = "JZTimerEventConnect";
    m_connectInfo.irList << irThis() << irLiteral(0) << irLiteral(0);
}

JZNodeTimerEvent::~JZNodeTimerEvent()
{
}

void JZNodeTimerEvent::setTimeOut(int ms)
{
    m_timeout = ms;
}

int JZNodeTimerEvent::timeOut()
{
    return m_timeout;
}

bool JZNodeTimerEvent::compiler(JZNodeCompiler* c, QString& error)
{        
    m_connectInfo.irList[1] = irLiteral(QVariant::fromValue(JZFunctionPointer(function().fullName())));
    m_connectInfo.irList[2] = irLiteral(m_timeout);
    
    return compilerSignal(c,error);
}

void JZNodeTimerEvent::saveToStream(QDataStream &s) const
{
    JZNodeSignalEvent::saveToStream(s);
    s << m_timeout;
}

void JZNodeTimerEvent::loadFromStream(QDataStream &s)
{
    JZNodeSignalEvent::loadFromStream(s);
    s >> m_timeout;
}

void JZTimerEventConnect(QObject *object,JZFunctionPointer slot_function,int ms)
{
    QTimer *timer = new QTimer(object);
    timer->connect(timer,&QTimer::timeout,object,[object,slot_function]
    {
        JZNodeObject *jzobj = qobjectToJZObject(object);
        JZNodeObjectPointer self(jzobj, false);
        QVariantList in,out;
        in << QVariant::fromValue(self);
        JZScriptInvoke(slot_function.function,in,out);
    });
    timer->start(ms);
}

//JZNodeEventFunctionInit
void JZNodeEventFunctionInit(JZScriptEnvironment *env)
{
    auto func_inst = env->functionManager();
    func_inst->registCFunction("JZTimerEventConnect",true,jzbind::createFuncion(JZTimerEventConnect));
}